package com.yeskery.nut.extend.mcp;

import com.yeskery.nut.application.ApplicationBlocker;
import com.yeskery.nut.application.ApplicationType;
import com.yeskery.nut.application.NutApplication;
import com.yeskery.nut.application.ServerEventContext;
import com.yeskery.nut.bean.ApplicationContext;
import com.yeskery.nut.extend.mcp.server.HttpMcpMessageRequestHandler;
import com.yeskery.nut.extend.mcp.server.McpStdioServerHandleListener;
import com.yeskery.nut.extend.mcp.server.StdioRequest;
import com.yeskery.nut.plugin.PluginBeanMetadata;
import com.yeskery.nut.util.StringUtils;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Collection;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * 基于Stdio的MCP Server插件，由于stdio需要时标准的输入/输出流，所以不需要输出任何日志。需要修改默认的日志处理器为FileHandler，同时还需要
 * 关闭banner输出，否则会导致MCP客户端无法正常解析数据。关闭示例：
 * <span>java -Dserver.logHandlers=java.util.logging.FileHandler -Dserver.printBanner=false</span>
 *
 * @author sprout
 * 2025-04-17 10:25
 */
public class McpStdioServerPlugin extends BaseMcpServerPlugin {

    /** 日志对象 */
    private static final Logger logger = Logger.getLogger(McpStdioServerPlugin.class.getName());

    /** 阻塞队列 */
    private final BlockingQueue<StdioRequest> blockingQueue = new ArrayBlockingQueue<>(24);

    /** 请求处理线程 */
    private final Thread requestThread;

    /** Stdio处理监听器 */
    private McpStdioServerHandleListener mcpStdioServerHandleListener;

    /**
     * 构建基于Stdio的MCP Server插件
     */
    public McpStdioServerPlugin() {
        requestThread = new Thread(new StdioRequestHandler());
        requestThread.start();
    }

    @Override
    public void beforeStart(ServerEventContext serverEventContext) {
        logger.info("MCP[Stdio] Started.");
    }

    @Override
    public void beforeClose(ServerEventContext serverEventContext) {
        requestThread.interrupt();
        if (mcpStdioServerHandleListener != null) {
            mcpStdioServerHandleListener.close();
        }
        // 关闭Nut应用
        NutApplication nutApplication = getApplicationContext().getBean(NutApplication.class);
        if (nutApplication.getApplicationMetadata().getApplicationType() == ApplicationType.STANDARD_BLOCKED) {
            ApplicationBlocker applicationBlocker = nutApplication.getApplicationContext().getBean(ApplicationBlocker.class);
            applicationBlocker.release();
        }
    }

    @Override
    public Collection<PluginBeanMetadata> getRegisterPluginBeanMetadata() {
        Collection<PluginBeanMetadata> registerPluginBeanMetadata = super.getRegisterPluginBeanMetadata();
        ApplicationContext applicationContext = getApplicationContext();
        HttpMcpMessageRequestHandler httpMcpMessageRequestHandler = new HttpMcpMessageRequestHandler(applicationContext,
                getMcpSessionManager(), getMcpServerCapability());
        mcpStdioServerHandleListener = new McpStdioServerHandleListener(applicationContext.getBean(NutApplication.class),
                httpMcpMessageRequestHandler, blockingQueue);
        registerPluginBeanMetadata.add(new PluginBeanMetadata("mcpStdioServerHandleListener",
                mcpStdioServerHandleListener, McpStdioServerHandleListener.class));
        return registerPluginBeanMetadata;
    }

    /**
     * 基于Stdio请求处理器
     * @author sprout
     * 2025-05-22 09:42
     */
    private class StdioRequestHandler implements Runnable {
        @Override
        public void run() {
            BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));

            try {
                String line;
                while ((line = reader.readLine()) != null) {
                    if (!StringUtils.isBlank(line)) {
                        StdioRequest request = new StdioRequest(line);
                        blockingQueue.put(request);
                    }
                }
            } catch (IOException e) {
                logger.log(Level.SEVERE, "Mcp Stdio Request Failure.", e);
            } catch (InterruptedException e) {
                logger.log(Level.SEVERE, "Mcp Stdio Request Interrupted.", e);
            } finally {
                try {
                    reader.close();
                } catch (IOException e) {
                    logger.log(Level.SEVERE, "Mcp Stdio Close Failure.", e);
                }
            }
        }
    }
}
